package design.pattern.application;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;

/**
 * @program JavaBase
 * @description: 命令模式 + 责任链模式 实现：linux 命令
 * @author: zhanglu
 * @create: 2020-05-22 09:29:00
 */
public class CommandAndChainDemo {

    public static void main(String[] args) throws IOException {
        Invoker invoker = new Invoker();
        while (true){
            System.out.println("#");
            String input = new BufferedReader(new InputStreamReader(System.in)).readLine();
            if(input.equals("quit") || input.equals("exit")){
                return;
            }
            System.out.println(invoker.exec(input));
        }
    }

}
//抽象命令类
abstract class CommandName{
    private CommandName nextOperator;
    public final String handleMessage(CommandVo vo){
        //处理结果
        String result = null;
        //实现
        if(vo.getParam().size() == 0 || vo.getParam().contains(this.getOperatorParam())){
            result = this.echo(vo);
        }else {
            if(this.nextOperator != null){
                result = this.nextOperator.handleMessage(vo);
            }else {
                result = "命令无法执行";
            }
        }
        return result;
    }
    //设置剩余参数由谁来处理
    public void setNext(CommandName nextOperator){
        this.nextOperator = nextOperator;
    }
    //每个处理者都要处理一个后缀参数
    protected abstract String getOperatorParam();
    //每个处理者都必须实现处理任务
    protected abstract String echo(CommandVo vo);
}
//抽象ls命令
abstract class AbstractLs extends CommandName{
    public final static String DEFAULE_PARAM = "";
    public final static String A_PARAM = "a";
    public final static String L_PARAM = "l";
}
//ls命令
class Ls extends AbstractLs{
    @Override
    protected String getOperatorParam() {
        return super.DEFAULE_PARAM;
    }
    @Override
    protected String echo(CommandVo vo) {
        return FileManager.ls(null);
    }
}
//ls -a 命令
class Ls_a extends AbstractLs{
    @Override
    protected String getOperatorParam() {
        return super.A_PARAM;
    }
    @Override
    protected String echo(CommandVo vo) {
        return FileManager.ls_a(null);
    }
}
//ls -l 命令
class Ls_l extends AbstractLs{
    @Override
    protected String getOperatorParam() {
        return super.L_PARAM;
    }
    @Override
    protected String echo(CommandVo vo) {
        return FileManager.ls_l(null);
    }
}
abstract class Command{
    public abstract String execute(CommandVo vo);
    //建立链表
    protected final List<? extends CommandName> buildChain(Class<? extends  CommandName> abstractClass){
        //取出所有的命令名下的子类
        List<Class> classes = ClassUtils.getSonClass(abstractClass);
        //存放命令的实例，并建立链表关系
        List<CommandName> commandNameList = new ArrayList<>();
        for (Class c : classes) {
            CommandName commandName = null;
            try {
                commandName = (CommandName) Class.forName(c.getName()).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            commandNameList.add(commandName);
        }
        Ls ls = new Ls();
        Ls_l ls_l = new Ls_l();
        Ls_a ls_a = new Ls_a();

        ls_l.setNext(ls_a);
        ls.setNext(ls_l);

        commandNameList.add(ls);
        return commandNameList;
    }
}
class LsCommand extends Command{
    @Override
    public String execute(CommandVo vo) {
        CommandName firstNode = super.buildChain(AbstractLs.class).get(0);
        return firstNode.handleMessage(vo);
    }
}
//任务分发类
class Invoker{
    //执行命令
    public String exec(String commandStr){
        String result = "";
        CommandVo commandVo = new CommandVo(commandStr);
        if(CommandEnum.getNames().contains(commandVo.getCommandName())){
            String className = CommandEnum.valueOf(commandVo.getCommandName()).getValue();
            try {
                Command command = (Command) Class.forName(className).newInstance();
                result = command.execute(commandVo);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else {
            result = "无法执行命令，请检查命令格式";
        }
        return result;
    }
}
class FileManager{
    //ls 命令
    public static String ls(String path){
        return "/file01/file02/file03/file03";
    }
    //ls -l命令
    public static String ls_l(String path){
        String str = "drw-rw-rw root system 1024 2009-8-20 10:23 file1\n";
        str = str + "drw-rw-rw root system 1024 2009-8-20 10:23 file2\n";
        str = str + "drw-rw-rw root system 1024 2009-8-20 10:23 file3";
        return str;
    }
    //ls -a命令
    public static String ls_a(String path){
        String str = ".\n..\nfile1\nfile2\nfile3"; return str;
    }
}
//命令对象 ls -l /usr 命令名、参数、操作数
class CommandVo{
    //定义参数名与参数的分割符号，一般是空格
    public final static String DIVIDE_FLAG = " ";
    //定义参数前的符号，Unix一般是-，如ls -la
    public final static String PREFIX = "-";
    //命令名，如 ls、du
    private String commandName;
    //参数列表
    private List<String> paramList = new ArrayList<>();
    //操作数列表
    private List<String> dataList = new ArrayList<>();
    //通过构造函数传递命令
    public CommandVo(String commandStr){
        if(commandStr != null && commandStr.length() != 0){
            String[] complexStr = commandStr.split(CommandVo.DIVIDE_FLAG);
            this.commandName = complexStr[0];
            //把参数放到List
            for (int i = 0; i < complexStr.length; i++) {
                String str = complexStr[i];
                //包含前缀符号，认为是参数
                if(str.indexOf(CommandVo.PREFIX) == 0){
                    this.paramList.add(str.replace(CommandVo.PREFIX, "").trim());
                }else {
                    this.dataList.add(str.trim());
                }
            }
        }else {
            System.out.println("命令为空");
        }
    }
    //得到命令
    public String getCommandName(){
        return this.commandName;
    }
    //获得参数
    public List<String> getParam(){
        if(this.paramList.size() == 0){
            //为了方便处理空参数
            this.paramList.add("");
        }
        return new ArrayList<>(new HashSet<>(this.paramList));
    }
    //获得操作数
    public List<String> getData(){
        return this.dataList;
    }
}
enum CommandEnum{
    ls("design.pattern.application.LsCommand")
    ;
    private String value;
    private CommandEnum(String value){
        this.value = value;
    }
    public String getValue(){
        return this.value;
    }
    //返回所有的enum对象
    public static List<String> getNames(){
        CommandEnum[] commandEnums = CommandEnum.values();
        List<String> names = new ArrayList<>();
        for (CommandEnum commandEnum : commandEnums) {
            names.add(commandEnum.name());
        }
        return names;
    }
}
//内部类则无效
class ClassUtils{
    //根据父类查找到所有的子类，默认情况是子类和父类都在同一个包名下
    public static List<Class> getSonClass(Class fatherClass){
        List<Class> resClass = new ArrayList<>();
        String packageName = fatherClass.getPackage().getName();
        List<Class> packageClasses = getClasses(packageName);
        for (Class packageClass : packageClasses) {
            if(fatherClass.isAssignableFrom(packageClass) && !fatherClass.equals(packageClass)){
                resClass.add(packageClass);
            }
        }
        return resClass;
    }
    //从一个包中查找出所有的类，在jar包中不能查找
    private static List<Class> getClasses(String packageName){
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = null;
        try {
            resources = classLoader.getResources(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        List<File> dirs = new ArrayList<>();
        while (resources.hasMoreElements()){
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        List<Class> classes = new ArrayList<>();
        for (File dir : dirs) {
            classes.addAll(findClasses(dir, packageName));
        }
        return classes;
    }
    private static List<Class> findClasses(File directory, String packageName){
        List<Class> classes = new ArrayList<>();
        if(!directory.exists()){
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if(file.isDirectory()){
                classes.addAll(findClasses(file, packageName + "." + file.getName().substring(0, file.getName().indexOf('.'))));
            }else if(file.getName().endsWith(".class")){
                try {
                    classes.add(Class.forName(packageName + "." + file.getName().substring(0, file.getName().indexOf('.'))));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return classes;
    }
}




